package com.answer.controller;

import cn.hutool.json.JSONUtil;
import com.answer.annotation.AuthCheck;
import com.answer.common.BaseResponse;
import com.answer.common.DeleteRequest;
import com.answer.common.ErrorCode;
import com.answer.constant.UserConstant;
import com.answer.entity.App;
import com.answer.entity.User;
import com.answer.entity.UserAnswer;
import com.answer.exception.BusinessException;
import com.answer.model.dto.userAnswer.UserAnswerAddRequest;
import com.answer.model.dto.userAnswer.UserAnswerQueryRequest;
import com.answer.model.dto.userAnswer.UserAnswerUpdateRequest;
import com.answer.model.enums.ReviewStatusEnum;
import com.answer.model.vo.UserAnswerVO;
import com.answer.service.AppService;
import com.answer.service.UserAnswerService;
import com.answer.service.UserService;
import com.answer.service.scoring.ScoringStrategyExecutor;
import com.answer.utils.ResultUtils;
import com.answer.utils.ThrowUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 用户答案接口
 */
@RestController
@RequestMapping("/userAnswer")
@Slf4j
public class UserAnswerController {

    @Resource
    private UserAnswerService userAnswerService;

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;

    @Resource
    private ScoringStrategyExecutor scoringStrategyExecutor;

    /**
     * 创建用户答案
     *
     * @param userAnswerAddRequest 用户答案添加请求对象，包含答案的相关信息
     * @param request               HTTP 请求对象，用于获取用户信息
     * @return 返回一个基础响应对象，包含新创建的答案的ID
     */
    @PostMapping("/add")
    public BaseResponse<Long> addUserAnswer(@RequestBody UserAnswerAddRequest userAnswerAddRequest, HttpServletRequest request) {
        // 校验参数是否为空
        ThrowUtils.throwIf(userAnswerAddRequest == null, ErrorCode.PARAMS_ERROR);
        // 将请求对象转换为用户答案对象
        UserAnswer userAnswer = new UserAnswer();
        BeanUtils.copyProperties(userAnswerAddRequest, userAnswer);
        // 处理选项列表
        List<String> choices = userAnswerAddRequest.getChoices();
        userAnswer.setChoices(JSONUtil.toJsonStr(choices));
        // 数据校验
        userAnswerService.validUserAnswer(userAnswer, true);
        // 校验应用是否存在
        Long appId = userAnswerAddRequest.getAppId();
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验应用是否通过审核
        if (!ReviewStatusEnum.PASS.equals(ReviewStatusEnum.getEnumByValue(app.getReviewStatus()))) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "应用未通过审核，无法答题");
        }
        // 设置用户ID
        User loginUser = userService.getLoginUser(request);
        userAnswer.setUserId(loginUser.getId());
        // 写入数据库
        boolean result = userAnswerService.save(userAnswer);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 获取新生成的答案ID
        long newUserAnswerId = userAnswer.getId();
        // 调用评分策略进行评分
        try {
            UserAnswer userAnswerWithResult = scoringStrategyExecutor.doScore(choices, app);
            userAnswerWithResult.setId(newUserAnswerId);
            userAnswerService.updateById(userAnswerWithResult);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "评分错误");
        }
        // 返回答案ID
        return ResultUtils.success(newUserAnswerId);
    }

    /**
     * 删除用户答案
     * <p>
     * 此方法用于处理用户删除答案的请求。它首先验证请求中的参数是否有效，然后检索当前登录的用户信息，
     * 根据提供的答案ID查找对应的用户答案。为了确保安全性，方法会检查请求删除的答案是否属于当前用户，
     * 或者当前用户是否为管理员。如果是，它会尝试从数据库中删除该答案。
     *
     * @param deleteRequest 包含要删除答案的ID的请求对象
     * @param request       原始HTTP请求对象，用于获取登录用户信息
     * @return 返回一个Boolean值的BaseResponse，表示删除操作是否成功
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteUserAnswer(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        // 验证请求参数是否有效
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 获取当前登录用户
        User user = userService.getLoginUser(request);
        // 获取请求中提供的答案ID
        long id = deleteRequest.getId();
        // 根据ID获取用户答案，检查答案是否存在
        UserAnswer oldUserAnswer = userAnswerService.getById(id);
        ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除答案
        if (!oldUserAnswer.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 尝试从数据库中删除答案
        boolean result = userAnswerService.removeById(id);
        // 如果删除失败，抛出异常
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回成功响应，携带结果为true表示删除成功
        return ResultUtils.success(true);
    }

    /**
     * 更新用户答案（仅管理员可用）
     * 该方法允许管理员更新用户的答案记录管理员通过提供用户答案的详细信息来实现更新操作
     * 注意：这是一个专属管理员的操作接口，普通用户无权访问
     *
     * @param userAnswerUpdateRequest 包含用户答案更新信息的请求体
     * @return 返回一个基础响应对象，其中包含一个Boolean值，指示更新操作是否成功
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateUserAnswer(@RequestBody UserAnswerUpdateRequest userAnswerUpdateRequest) {
        // 校验请求参数是否合法
        if (userAnswerUpdateRequest == null || userAnswerUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 将请求对象转换为实体对象，以便操作数据库
        UserAnswer userAnswer = new UserAnswer();
        BeanUtils.copyProperties(userAnswerUpdateRequest, userAnswer);
        // 处理选择答案的列表，将其转换为JSON字符串存储
        List<String> choices = userAnswerUpdateRequest.getChoices();
        userAnswer.setChoices(JSONUtil.toJsonStr(choices));
        // 对用户答案进行数据校验
        userAnswerService.validUserAnswer(userAnswer, false);
        // 根据ID检查用户答案是否存在
        long id = userAnswerUpdateRequest.getId();
        UserAnswer oldUserAnswer = userAnswerService.getById(id);
        ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
        // 更新用户答案到数据库
        boolean result = userAnswerService.updateById(userAnswer);
        // 检查更新操作是否成功
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回更新成功的结果
        return ResultUtils.success(true);
    }

    /**
     * 根据 id 获取用户答案（封装类）
     * 本方法通过用户答案的ID来获取一个封装好的用户答案对象。首先，它会校验传入的ID是否有效，
     * 如果ID无效，则抛出参数错误异常。接着，它会尝试从数据库中查询对应的用户答案，如果找不到，
     * 则抛出未找到错误异常。最后，它会将查询到的用户答案封装成一个统一的响应对象并返回。
     *
     * @param id 用户答案的唯一标识符
     * @return 包含用户答案信息的统一响应对象
     */
    @GetMapping("/get/vo")
    public BaseResponse<UserAnswerVO> getUserAnswerVOById(long id) {
        // 校验ID有效性
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        UserAnswer userAnswer = userAnswerService.getById(id);
        // 校验查询结果
        ThrowUtils.throwIf(userAnswer == null, ErrorCode.NOT_FOUND_ERROR);
        // 获取封装类
        return ResultUtils.success(userAnswerService.getUserAnswerVO(userAnswer));
    }

    /**
     * 分页获取用户答案列表（仅管理员可用）
     * 该方法通过分页的方式获取用户答案列表，旨在为管理员提供一个高效的数据查询手段。
     * 通过指定当前页数和每页的大小，可以有效地管理大量用户答案数据的展示和处理。
     *
     * @param userAnswerQueryRequest 包含查询参数的请求体，用于指定分页和筛选条件
     * @return 返回一个包含用户答案分页数据的响应对象，其中包含的数据页码和每页数据量由请求体指定
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<UserAnswer>> listUserAnswerByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest) {
        // 获取请求中的当前页数和每页大小
        long current = userAnswerQueryRequest.getCurrent();
        long size = userAnswerQueryRequest.getPageSize();
        // 调用服务层方法实现分页查询，传入分页参数和查询条件
        Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
                userAnswerService.getQueryWrapper(userAnswerQueryRequest));
        // 返回成功响应，携带分页后的用户答案列表
        return ResultUtils.success(userAnswerPage);
    }

    /**
     * 分页获取用户答案列表（封装类）
     * @param userAnswerQueryRequest 用户答案查询请求封装类，包含分页和筛选条件
     * @return 返回一个BaseResponse对象，其中包含分页的用户答案列表封装类
     * 说明：本方法通过分页的方式获取用户答案列表，并返回一个封装好的响应对象。
     * 使用了@PostMapping注解指定请求路径和处理方式为POST。
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<UserAnswerVO>> listUserAnswerVOByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest) {
        // 获取当前页码和每页显示数量
        long current = userAnswerQueryRequest.getCurrent();
        long size = userAnswerQueryRequest.getPageSize();

        // 限制爬虫：如果每页显示数量大于20，则抛出异常
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);

        // 查询数据库：使用分页查询用户答案
        Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
                userAnswerService.getQueryWrapper(userAnswerQueryRequest));

        // 获取封装类：将查询结果转换为用户答案列表封装类，并返回成功响应
        return ResultUtils.success(userAnswerService.getUserAnswerVOPage(userAnswerPage));
    }

    /**
     * 分页获取当前登录用户创建的用户答案列表
     * <p>
     * 该方法通过分页的方式，查询当前登录用户所创建的用户答案列表
     * 它首先校验请求参数的合法性，然后补充查询条件以仅查询当前登录用户的数据
     * 接着限制单页数据量以防止爬虫行为，最后查询数据库并返回结果
     *
     * @param userAnswerQueryRequest 用户答案查询请求对象，包含分页和筛选条件
     * @param request                HTTP请求对象，用于获取当前登录用户信息
     * @return 返回一个基础响应对象，包含用户答案的分页数据
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<UserAnswerVO>> listMyUserAnswerVOByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest,
                                                                         HttpServletRequest request) {
        // 校验查询请求参数是否为空
        ThrowUtils.throwIf(userAnswerQueryRequest == null, ErrorCode.PARAMS_ERROR);

        // 补充查询条件，只查询当前登录用户的数据
        User loginUser = userService.getLoginUser(request);
        userAnswerQueryRequest.setUserId(loginUser.getId());

        // 获取当前页码和每页显示数量
        long current = userAnswerQueryRequest.getCurrent();
        long size = userAnswerQueryRequest.getPageSize();

        // 限制每页数据量最大为20条，防止爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);

        // 根据查询条件和分页参数，从数据库中查询用户答案列表
        Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
                userAnswerService.getQueryWrapper(userAnswerQueryRequest));

        // 将查询结果转换为所需的封装类格式，并返回成功响应
        return ResultUtils.success(userAnswerService.getUserAnswerVOPage(userAnswerPage));
    }

    /**
     * 编辑用户答案（给用户使用）
     *
     * @param userAnswerEditRequest 用户答案编辑请求体，包含需要编辑的答案信息
     * @param request                HTTP请求对象，用于获取登录用户信息
     * @return 返回一个布尔值，表示编辑操作是否成功
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editUserAnswer(@RequestBody UserAnswerUpdateRequest userAnswerEditRequest, HttpServletRequest request) {
        // 校验参数是否有效，如果请求体为空或ID不合法，则抛出业务异常
        if (userAnswerEditRequest == null || userAnswerEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 将请求体转换为用户答案实体类，准备存入数据库
        UserAnswer userAnswer = new UserAnswer();
        BeanUtils.copyProperties(userAnswerEditRequest, userAnswer);
        // 处理选择题答案，将其转换为JSON字符串存储
        List<String> choices = userAnswerEditRequest.getChoices();
        userAnswer.setChoices(JSONUtil.toJsonStr(choices));
        // 对用户答案进行数据校验·
        userAnswerService.validUserAnswer(userAnswer, false);
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 根据ID获取原有答案，判断是否存在
        long id = userAnswerEditRequest.getId();
        UserAnswer oldUserAnswer = userAnswerService.getById(id);
        ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
        // 检查权限，只有本人或管理员才能编辑答案
        if (!oldUserAnswer.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 更新数据库中的用户答案
        boolean result = userAnswerService.updateById(userAnswer);
        // 如果更新失败，则抛出操作异常
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回编辑成功的结果
        return ResultUtils.success(true);
    }

}
